package tests;

import java.io.File;
import java.io.IOException;
import java.security.InvalidParameterException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import javax.activity.InvalidActivityException;

public class TestHelper {

	public static File		RootDir	= new File(System.getProperty("user.dir"), "Photos");
	public static String	JPG		= "jpg";
	public static String	TXT		= "txt";

	public static void cleanDirectory(File rootDirectory) throws InvalidActivityException, IOException, InterruptedException {
		deleteDirectoryRecursive(rootDirectory);
		mkdirs(rootDirectory);
		java.lang.Thread.sleep(10);
	}

	/**
	 * Delete the given directory. Before a directory can be deleted, we need to recursively remove all files it contains. Recursively
	 * navigate to the deepest directory, delete it's files, then delete the directory.
	 * 
	 * @param directory the directory that is to be deleted.
	 * @throws InvalidActivityException
	 */
	private static void deleteDirectoryRecursive(File directory) throws InvalidActivityException {
		if (directory.exists()) {
			for (File file : directory.listFiles()) {
				if (file.isDirectory())
					deleteDirectoryRecursive(file);
				else {
					if (!file.delete()) {
						System.out.println("TestHelper.deleteDirectory: Failed to delete file: " + file.getPath());
						sleep(1); // TODO: If this is still happening, then remove the sleep
						file.delete(); // try again... if it fails again, not sure what we can do
					}
				}
			}

			sleep(10);

			if (!directory.delete()) {
				System.out.println("TestHelper.deleteDirectory: Can't delete dir: " + directory.getPath() + ".   Trying one more time.");
				sleep(10); // TODO: If this is still happening, then remove the sleep
				if (!directory.delete()) {
					System.out.println("TestHelper.deleteDirectory: Still can't delete dir.");
					throw new InvalidActivityException(
							"TestHelper.deleteDirectory: The temp directory needs to be deleted for the tests" + directory.getPath());
				}
			}
		}
	}

	/**
	 * Creates an empty file in the given root directory.
	 * 
	 * @param rootDir The root directory where the files are created
	 * @param prefix The prefix that forms part of the file name
	 * @param number The unique number that goes after the prefix (preceded with a space automatically)
	 * @param postfix The text that goes after the unique number (preceded with a space)
	 * @param extension The file extension
	 * @return returns The file that is created. Returns null on error
	 * @throws IOException
	 */
	public static File createFile(File rootDir, String prefix, int number, String postfix, String extension) throws IOException {

		mkdirs(rootDir);

		String pictureFileName = String.format("%s%d %s.%s", prefix, number, postfix, extension);
		File tmpFile = new File(rootDir, pictureFileName);

		createFile(tmpFile);

		return tmpFile;
	}

	private static void createFile(File tmpFile) throws IOException {
		if (tmpFile.exists()) {
			System.out.println("Error: TestHelper.createFile - Failed to create a test file: " + tmpFile.getPath());
			if (!tmpFile.canWrite())
				System.out.println("Error: TestHelper.createFile - The file can't be written to: " + tmpFile.getPath());
		}

		try {
			if (tmpFile.createNewFile()) {
				if (!tmpFile.canWrite())
					System.out.println("Error: TestHelper.createFile - The file can't be written to: " + tmpFile.getPath());
			}
		}
		catch (IOException e) {
			e.printStackTrace();
			throw e;
		}
	}

	public static void createPhotos(File rootDir, String extension, int numerOfFiles) throws IOException {
		mkdirs(rootDir);

		for (int i = 1; i <= numerOfFiles; ++i) {
			createFile(rootDir, "2010_11_", i, "21.47.39", extension);
		}
	}

	private static void sleep(int milliseconds) {
		try {
			java.lang.Thread.sleep(milliseconds);
		} // TODO: If this is still happening, then remove the sleep
		catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	private static void mkdirs(File directory) throws InvalidActivityException {
		if (directory.isFile()) {
			throw new InvalidActivityException("TestHelper.mkdirs: The directory is not a directory" + directory.getPath());
		}

		if (!directory.exists()) {

			sleep(1);

			if (!directory.mkdirs()) {
				System.out.println("TestHelper.mkdirs: Can't create directory:\n\t" + directory.getPath() + "\n\tSleeping(10)...");
				java.lang.Thread.dumpStack();
				System.out.println("");
				sleep(1);
				System.out.println("\tTestHelper.mkdirs: Retrying...");

				if (!directory.mkdirs())
					throw new InvalidActivityException("TestHelper.mkdirs: Failed to create directory:\n\t" + directory.getPath());
			}
		}
	}

	private static String createDateString(String format, int y, int m, int d) throws InvalidParameterException {
		// have to initialise here based on the day because if it is <0, it will wrap to the previous month by the number of days
		// when we'd prefer it to just not take the day into account.
		String dateString = (d > 0) ? String.format("%d-%d-%d", y, m, d) : String.format("%d-%d", y, m);
		SimpleDateFormat formatter = (d > 0) ? new SimpleDateFormat("yyyy-MM-dd") : new SimpleDateFormat("yyyy-MM");
		Date date = null;

		try {
			date = formatter.parse(dateString);
		}
		catch (ParseException e) {
			e.printStackTrace();
			throw new InvalidParameterException(e.getMessage());
		}

		SimpleDateFormat outputFormatter = new SimpleDateFormat(format);
		return outputFormatter.format(date);
	}

	/**
	 * Format the given date into a string with the given format.
	 * 
	 * @param format that the output is to be in
	 * @param date the date
	 * @return
	 */
	public static String formatDate(String format, Date date) {
		SimpleDateFormat outputFormatter = new SimpleDateFormat(format);
		return outputFormatter.format(date);
	}

	/**
	 * Format the given date with the yyyy_MM_dd format
	 * 
	 * @param date the date to be formatted
	 * @return the formatted date.
	 */
	public static String formatDate(Date date) {
		return formatDate("yyyy_MM_dd", date);
	}

	/**
	 * Create a date based directory in the given root directory based on the date given. The postfix is appended at the end.
	 * 
	 * @param rootDir The <b><i>root</i></b> directory where sub directories.
	 * @param format The <b><i>format</i></b> of the date. <i>eg yyyyMMdd</i>.
	 * @param y The <b><i>year</i></b> that the folder is to include.
	 * @param m The <b><i>month</i></b> that the folder is to include.
	 * @param d The <b><i>day</i></b> that the folder is to include.
	 * @param postfix A postfix attached to the end of each directory. Use null or empty string if no postfix is required.
	 * @return returns the {@link File} that is created as a result
	 * @throws InvalidActivityException
	 */
	private static File createDateBasedDir(File rootDir, String format, int y, int m, int d, String postfix)
			throws InvalidActivityException {
		File file = new File(rootDir, (createDateString(format, y, m, d) + ((postfix != null) ? postfix : "")));

		mkdirs(file);

		return file;
	}

	/**
	 * Creates a set of directories in the given root directory with a specific date based format
	 * 
	 * @param rootDir The <b><i>root</i></b> directory where sub directories.
	 * @param format The <b><i>format</i></b> of the date. <i>eg yyyyMMdd</i>.
	 * @param y The <b><i>year</i></b> that the folder is to include.
	 * @param m The <b><i>month</i></b> that the folder is to include.
	 * @param days The number of folders that will be created. No checks are done on value of this, so the resulting date may be incorrect
	 *            for the given year/month.
	 * @param postfix A postfix attached to the end of each directory. Use null or empty string if no postfix is required.
	 * @return A collection of all of the directories that were created.
	 * @throws InvalidActivityException
	 */
	public static File[] createDateBasedPhotoDirs(File rootDir, String format, int y, int m, int days, String postfix)
			throws InvalidActivityException {
		mkdirs(rootDir);

		ArrayList<File> files = new ArrayList<File>();

		for (int i = 1; i <= days; i++) {
			files.add(createDateBasedDir(rootDir, format, y, m, i, postfix));
		}

		return files.toArray(new File[files.size()]);
	}

	/**
	 * Creates a directory in the given root directory with a specific date based format for a given year and month
	 * 
	 * @param rootDir The <b><i>root</i></b> directory where sub directories.
	 * @param format The <b><i>format</i></b> of the date. <i>eg yyyyMM</i>. Do not use formats that include the day.
	 * @param y The <b><i>year</i></b> that the folder is to include.
	 * @param m The <b><i>month</i></b> that the folder is to include.
	 * @param count The <b><i>count</i></b> of albums generated starting from the given month. A count that forces the month exceed 12 will
	 *            wrap over to the next year.
	 * @return the albums (Files) created.
	 * @throws InvalidActivityException
	 */
	public static File[] createDateBasedPhotoDirs(File rootDir, String format, int y, int m, int count) throws InvalidActivityException {
		ArrayList<File> files = new ArrayList<File>();
		for (int i = m; i < (m + count); i++) {
			files.add(createDateBasedDir(rootDir, format, y, i, -1, null));
		}

		return files.toArray(new File[files.size()]);
	}
}
